package com.blog.Utils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 任浩
 * @version 1.0
 */
@Component
public class SensitiveWordUtils {
    //我们将敏感词替换为*号

    //当前节点为根节点。
    private TrieNode rootNode = new TrieNode();

    //进行敏感词的初始化，把所有的敏感词都加载到前缀树中来。
    @PostConstruct  //这个注解代表当前方法在类被加载的时候，就调用
    private void init() {
        //从资源文件中读取，所有的敏感词。
        try {
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("templates/SensitiveWord.txt");
            //将字节流转化为字符流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            //进行读取敏感词。我们可以一行一行的读，因为一个敏感词一行
            String text = bufferedReader.readLine();
            while (text != null){
                //把当前的敏感词，添加进入前缀树中。
                addSensitiveWord(text);
                text = bufferedReader.readLine();
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 该方法是将敏感词添加进入前缀树.
     * @param text
     */
    public void addSensitiveWord(String text){
        //定义一个临时节点。
        TrieNode temNode = this.rootNode;

        for(int i = 0; i < text.length(); i++){
            char c = text.charAt(i);
            //先判断当前节点的子节点是否有当前第一个敏感字的。
            TrieNode subNode = temNode.getNode(c);

            //如果当前节点为null，那么就把敏感字加入前缀树
            if(subNode == null){
                subNode = new TrieNode();
                //把当前敏感字加入进去。
                temNode.setNode(c,subNode);
            }

            //不为null，那么代表有当前敏感词，不需要加入。直接移动到下一个子节点即可。然后一直重复判断，直到到的退出条件
            temNode = subNode;
            if(i == text.length() - 1){
                //设置为敏感词结束标志
                temNode.setSensitiveWord(true);
            }
        }
    }

    //这是进行敏感词过滤的方法。
    public StringBuffer filter(String text){
        //设置前缀树的指针。
         TrieNode temNode = this.rootNode;
        //设置遍历过滤字符串的双指针
        int begin = 0;
        int position = 0;

        StringBuffer stringBuffer = new StringBuffer();

        while (begin < text.length() && position < text.length()){
            char c = text.charAt(position);

            //判断当前字符是否是敏感词,如果不是就直接让双指针指向下一个位置。
            TrieNode subNode = temNode.getNode(c);
            if(subNode == null){
                begin += 1;
                position =begin;
                stringBuffer.append(c);
            }
            else if(subNode != null){ //如果当前节点不为空，说明当前字符是疑似敏感字段。
                //先判断当前敏感字是否有标记敏感字结束标志。 并且把前缀树的指针重新置回头结点。
                if(subNode.getIsSensitiveWord() == true){
                    for(int tem = begin ; tem<= position; tem++ ){
                        stringBuffer.append("*");
                    }
                    position += 1;
                    begin = position;
                    temNode = this.rootNode;
                }
                else {
                    //当前有敏感词并且没有结束符标志。position向下移动。
                    //将当前的前缀树的指针，向下移动一位
                    temNode = subNode;
                    //保存begin指针不变，将position指针向下移动一位
                    position += 1;
                }
            }
        }
        return stringBuffer;
    }

    //定义一个内部类，用于定义一个前缀树。使用前缀树来保存敏感词。
    //定义的这个类为成员内部类
    private class TrieNode{

        //定义当前子节点是否为敏感词的结束标志。
        private boolean isSensitiveWord = false;
        //定义该节点的子节点，使用map的原因是该节点可能有多个子节点。 key保存当前节点的敏感词的值，TrieNode是子节点
        private Map<Character,TrieNode> node = new HashMap<>();

        public void setSensitiveWord(boolean isSensitiveWord){
            this.isSensitiveWord = isSensitiveWord;
        }

        public void setNode(Character sensitiveWord ,TrieNode subNode){
            node.put(sensitiveWord,subNode);
        }

        //得到当前是否为敏感词的状态
        public boolean getIsSensitiveWord(){
            return  isSensitiveWord;
        }

        //根据敏感词，得到当前的节点.
        public TrieNode getNode(Character sensitiveWord){
           return node.get(sensitiveWord);
        }
    }
}
